package prefuse.decisiontree;

import java.util.Iterator;
import java.util.LinkedList;

import javax.swing.JOptionPane;

import prefuse.decisiontree.api.DecisionNodeInterface;
import prefuse.decisiontree.api.Edge;



/**
 * A DecisionNode represents a Tree Node in a Decision Tree
 * A DecisionNode contains:
 *      - TreeNodeValue, the node value
 *      - LinktedList<Edge> that contain the children of the node
 *      - Edge, this Decision Node is its destination.
 * 
 * @author Alan Yeung
 * @editor WenyiAn
 */
public class DecisionNode implements DecisionNodeInterface{
    
    private Edge sourceEdge;
    private double time;
    private String variableName;
    private boolean selected;
    private LinkedList<Edge> childrenEdgeList;
    private int treeNodeID;
    private Edge bestChildEdge;
    private double rollBackValue;
    private String NodeType = "DecisionNode";

    public DecisionNode(int theTreeNodeID) {

        this.sourceEdge = null;
        this.time = Double.NaN;
        this.variableName = null;
        this.selected = false;
        this.childrenEdgeList = new LinkedList<Edge>();
        this.treeNodeID = theTreeNodeID;
        this.rollBackValue = Double.NaN;
        this.bestChildEdge = null;
    }
    /**
     * return the type of Node
     * @return NodeType
     */
    public String getNodeType(){
    	return this.NodeType;
    }
    /**
     * method to return  value
     * @return  value in double
     */
    public double getRollBackValue()
    {
        return this.rollBackValue;
    }

    /**
     * method to set  value
     * @param double that represents  value
     */
    public void setRollBackValue(double newValue)
    {
        this.rollBackValue = newValue;
    }
  

    /**
     * retrieves bestChildEdge
     * @return
     */
    public Edge getBestChildEdge()
    {
        return this.bestChildEdge;
    }

    /**
     * assigns the bestChildEdge
     * @param theBestChildEdge
     */
    public void setBestChildEdge(Edge theBestChildEdge)
    {
        this.bestChildEdge = theBestChildEdge;
    }

    /**
     * retrieves the treeNode ID
     * @return treeNodeID in int
     */
    public int getTreeNodeID()
    {
        return this.treeNodeID;
    }

    /**
     * Indicates if the Chance node is selected
     * @return  true if selected
     *          false if not selected
     */
    public boolean isSelected()
    {
        return this.selected;
    }

    /**
     * Assigns the current Node as selected or not selected
     * @param   true if selected
     *          false if not selected
     * @param newSelected
     */
    public void setSelected(boolean newSelected)
    {
        this.selected = newSelected;
    }

    /**
     * Retrieves the time of the current Node
     * @return
     */
    public double getTime()
    {
        return this.time;
    }

        /**
     * Retrieve the time value in double format
     *
     * @precondition the time must be >=0
     * @param sourceEdge An Edge with this node as the destination.
     * @see Edge
     */
    public void setTime(double newTime) throws IllegalArgumentException
    {
        if(newTime < 0)
        {
        	JOptionPane.showMessageDialog(null,"The given Time is invalid. Please Ensure the assigned time is greater than 0");
            throw new IllegalArgumentException("The given Time is invalid. Please Ensure the assigned time is greater than 0");
        }
        else
        {
            this.time = newTime;
        }
    }

    /**
     * Retrieves the variable name of the chance node
     * @return the variable name in string format
     */
    public String getVariableName()
    {
        return this.variableName;
    }

    /**
     * Assigns the Variable name of the intermediate tree node
     */
    public void setVariableName(String newVariableName)
    {
        this.variableName = newVariableName;
    }


    /**
     * Remove an Edge from this node to disconnect a child. If the Edge does not exist, do nothing.
     * Note: No exception is thrown if the Edge was not a child, as the removal is considered successful.
     *
     * @postcondition iterator returned from getChildren will not contain childToRemove
     *
     * @param childToRemove An Edge to attempt to remove from this node.
     * @see Edge
     */
    public boolean removeChildEdge(Edge childToRemove){
        return this.childrenEdgeList.remove(childToRemove);
    }

/**
     * Adds an Edge to this node to connect a child. If the Edge already exists
     * in the node, do nothing.
     *
     * Throws exception if:
     *          - the Edge's sourcce is not the current node.
     *          - the Edge's destination is equal to the current node
     *          - the Edge's destination is equal to null
     *          - the Edge is null
     *
     *
     * @postcondition iterator returned from getChildren will contain childToAdd
     *
     * @param childToAdd An Edge to be added to this node
     * @return
     */
    public boolean addChildEdge(Edge childToAdd) throws IllegalArgumentException
    {
        if (childToAdd == null
                || childToAdd.getSourceNode() != this
                || childToAdd.getDestinationNode() == this
                || childToAdd.getDestinationNode() == null)
        {
        	 JOptionPane.showMessageDialog(null,"The new child \"" + childToAdd + "\" is invalid");
             throw new IllegalArgumentException("The new child \"" + childToAdd + "\" is invalid");
        }

        if(this.childrenEdgeList.contains(childToAdd))
        {
            //do nothing
            return false;
        }
        else
        {
            return this.childrenEdgeList.add(childToAdd);
        }
    }

    /**
     * Retrieve the Edges which are children of this node.
     *
     * @return An Iterator over Edges which have this node as their Source.
     * @see Iterator
     * @see Edge
     */
    public Iterator<Edge> getChildEdgeList(){
        return this.childrenEdgeList.iterator();
    }

    /**
     * Retrieve the Edge which connects this TreeNode to its parent TreeNode.
     *
     * @return An Edge with this TreeNode as its destination. Will return NULL if no source Edge exists.
     * @see Edge
     */
    public Edge getSourceEdge(){
        return this.sourceEdge;
    }

    /**
     * Set a new source Edge for this TreeNode. The Edge must have this TreeNode as its destination.
     * throw IllegalArgumentException if
     *      - newSourceEdge is null
     *      - the destination of the Edge is not the current node
     *      - the newSourceEdge is a child Edge of the current NODe
     * @postcondition getSourceEdge returns the value provided as an argument
     *
     * @param sourceEdge An Edge with this node as the destination.
     * @see Edge
     */
    public void setSourceEdge(Edge newSourceEdge) throws IllegalArgumentException{

        if (newSourceEdge == null)
        {
        	JOptionPane.showMessageDialog(null,"Source Edge of a ChanceNode cannot be null");
            throw new IllegalArgumentException("Source Edge of a ChanceNode cannot be null");
        }
        else if(newSourceEdge.getDestinationNode() != this)
        {
        	JOptionPane.showMessageDialog(null,"Source Edge destination node does not equal to destination node");
            throw new IllegalArgumentException("Source Edge destination node does not equal to destination node");
        }
        else if(this.childrenEdgeList.contains(newSourceEdge))
        {
        	JOptionPane.showMessageDialog(null,"The SourceEdge is a child Edge of the Decision Node");
            throw new IllegalArgumentException("The SourceEdge is a child Edge of the Decision Node");
        }

        this.sourceEdge = newSourceEdge;
    }
}
